{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## モジュールクラス\n",
    "\n",
    "* モジュールクラス\n",
    "    * Rubyでプログラムを書く上で有用なメソッドが多く定義されている\n",
    "    * ClassクラスはこのModuleクラスを継承している\n",
    "        * すべてのクラスでメソッドが利用可能\n",
    "    * モジュールはincludeメソッドを使って任意のクラスにインクルードできる"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"bar\""
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "module MyModule\n",
    "  def foo\n",
    "    'bar'\n",
    "  end\n",
    "end\n",
    "\n",
    "class MyClass\n",
    "  include MyModule\n",
    "end\n",
    "\n",
    "MyClass.new.foo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 定義されている関数に関するメソッド\n",
    "\n",
    "* メソッド\n",
    "    * Module.constants\n",
    "        * その時点で定義されている定数を取得\n",
    "    * constants\n",
    "        * 特定のクラスやモジュールで定義されている定数を取得\n",
    "    * const_defined?\n",
    "        * 指定された定数が定義されているかを確認\n",
    "    * const_get\n",
    "        * 定義されている定数の値を取り出す\n",
    "    * const_set\n",
    "        * 新たに定数を定義する\n",
    "    * remove_const\n",
    "        * 定数を取り除く\n",
    "    * const_missing\n",
    "    * ancestors\n",
    "        * クラスやモジュールの祖先クラスとインクルードしているモジュールの一覧を返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, :NIL, :Data, :TrueClass, :TRUE, :FalseClass, :FALSE, :Encoding, :Comparable, :Enumerable, :String, :Symbol, :Exception, :SystemExit, :SignalException, :Interrupt, :StandardError, :TypeError, :ArgumentError, :IndexError, :KeyError, :RangeError, :ScriptError, :SyntaxError, :LoadError, :NotImplementedError, :NameError, :NoMethodError, :RuntimeError, :SecurityError, :NoMemoryError, :EncodingError, :SystemCallError, :Errno, :UncaughtThrowError, :ZeroDivisionError, :FloatDomainError, :Numeric, :Integer, :Fixnum, :Float, :Bignum, :Array, :Hash, :ENV, :Struct, :RegexpError, :Regexp, :MatchData, :Marshal, :Range, :IOError, :EOFError, :IO, :STDIN, :STDOUT, :STDERR, :ARGF, :FileTest, :File, :Dir, :Time, :Random, :Signal, :Proc, :LocalJumpError, :SystemStackError, :Method, :UnboundMethod, :Binding, :Math, :GC, :ObjectSpace, :Enumerator, :StopIteration, :RubyVM, :Thread, :TOPLEVEL_BINDING, :ThreadGroup, :ThreadError, :ClosedQueueError, :Mutex, :Queue, :SizedQueue, :ConditionVariable, :Process, :Fiber, :FiberError, :Rational, :Complex, :RUBY_VERSION, :RUBY_RELEASE_DATE, :RUBY_PLATFORM, :RUBY_PATCHLEVEL, :RUBY_REVISION, :RUBY_DESCRIPTION, :RUBY_COPYRIGHT, :RUBY_ENGINE, :RUBY_ENGINE_VERSION, :TracePoint, :ARGV, :Gem, :DidYouMean, :RbConfig, :CROSS_COMPILING, :StringIO, :MonitorMixin, :Monitor, :BundlerVendoredPostIt, :Pathname, :Bundler, :Etc, :FileUtils, :URI, :Digest, :Delegator, :SimpleDelegator, :Tempfile, :OptionParser, :OptParse, :Set, :SortedSet, :TSort, :Forwardable, :SingleForwardable, :MultiJson, :IRuby, :Logger, :MimeMagic, :OpenSSL, :SecureRandom, :In, :Out, :ZMQ, :BasicSocket, :Socket, :SocketError, :IPSocket, :TCPSocket, :TCPServer, :UDPSocket, :UNIXSocket, :UNIXServer, :Addrinfo, :ZL, :JSON, :OpenStruct, :Singleton, :PrettyPrint, :PP, :Pry, :MethodSource, :Shellwords, :CodeRay, :Slop, :Readline, :MyModule, :MyClass, :RUBYGEMS_ACTIVATION_MONITOR]\n",
      "\n",
      "Stringクラスの定数\n",
      "[:Hoge, :Extend]\n",
      "true\n",
      "JSON::Ext::Generator::GeneratorMethods::String::Extend\n",
      "[:Hoge, :Extend]\n",
      "hogehoge\n",
      "\n",
      "1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[MyClass, MyModule, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "puts Module.constants\n",
    "puts\n",
    "\n",
    "puts \"Stringクラスの定数\"\n",
    "puts String.constants\n",
    "puts String.const_defined?(:Extend)\n",
    "puts String.const_get(:Extend)\n",
    "#String.const_set(:Hoge,'hogehoge')\n",
    "puts String.constants\n",
    "puts String.const_get(:Hoge)\n",
    "puts\n",
    "\n",
    "class MyClass\n",
    "  MYCONST = 1\n",
    "  puts MYCONST\n",
    "  remove_const(:MYCONST)\n",
    "end\n",
    "MyClass.constants\n",
    "\n",
    "MyClass.ancestors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "source": [
    "## メソッドの設定\n",
    "\n",
    "* メソッド\n",
    "    * instance_methods\n",
    "        * インスタンスに設定されているpublicメソッドとprotectedメソッドの一覧を取得する\n",
    "    * public_instance_methods, protected_instance_methods, private_instance_methods\n",
    "        * それぞれの可視性のインスタンスメソッドの一覧を取得する\n",
    "    * public, protected, private\n",
    "        * 可視性を設定する\n",
    "    * attr_reader, attr_writer, attr_accessor, attr\n",
    "        * インスタンスの属性としてインスタンス変数と読み取りメソッド、書き込みメソッドを定義する\n",
    "            * attr_reader : 読み込みメソッド\n",
    "            * attr_writer : 書き込みメソッド\n",
    "            * attr\n",
    "                * 2番目の引数\n",
    "                    * true : 読み込みと書き込み両方のメソッド\n",
    "                    * 設定無し or false : 読み込みメソッド\n",
    "            * attr_reader : 読み込みと書き込み両方のメソッド\n",
    "    * alias_methods\n",
    "        * メソッドの別名を定義\n",
    "            * aliasとの違い\n",
    "                * メソッドであり、メソッド名を文字列で指定できる\n",
    "                * aliasは予約語。直接メソッドを指定する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:fill, :assoc, :rassoc, :uniq, :uniq!, :compact, :compact!, :flatten, :to_h, :flatten!, :shuffle!, :shuffle, :include?, :combination, :repeated_permutation, :permutation, :product, :sample, :repeated_combination, :bsearch_index, :bsearch, :select!, :&, :*, :+, :-, :pretty_print_cycle, :sort, :count, :find_index, :select, :reject, :collect, :map, :pack, :first, :any?, :reverse_each, :zip, :take, :take_while, :drop, :drop_while, :cycle, :insert, :|, :index, :rindex, :replace, :clear, :<=>, :<<, :==, :[], :[]=, :pretty_print, :reverse, :empty?, :eql?, :concat, :reverse!, :shelljoin, :inspect, :delete, :length, :size, :each, :slice, :slice!, :to_ary, :to_a, :to_s, :dig, :hash, :at, :fetch, :last, :push, :pop, :shift, :unshift, :frozen?, :each_index, :join, :rotate, :rotate!, :sort!, :collect!, :map!, :sort_by!, :keep_if, :values_at, :delete_at, :delete_if, :reject!, :transpose, :to_json, :find, :entries, :sort_by, :grep, :grep_v, :detect, :find_all, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :all?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :each_entry, :each_slice, :each_cons, :each_with_object, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :lazy, :to_set, :pry, :__binding__, :pretty_print_instance_variables, :pretty_print_inspect, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :public_method, :singleton_method, :is_a?, :extend, :define_singleton_method, :method, :to_enum, :enum_for, :===, :=~, :!~, :respond_to?, :freeze, :display, :object_id, :send, :gem, :pretty_inspect, :nil?, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :public_methods, :singleton_methods, :!, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]\n"
     ]
    }
   ],
   "source": [
    "puts Array.instance_methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### メソッドの可視性の変更"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FOO\n"
     ]
    }
   ],
   "source": [
    "### privateを後からpublicに変更している\n",
    "class MyClass\n",
    "  private\n",
    "  def foo\n",
    "    puts 'FOO'\n",
    "  end\n",
    "  public :foo\n",
    "end\n",
    "\n",
    "a = MyClass.new\n",
    "a.foo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### インスタンスの属性を設定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "200\n"
     ]
    }
   ],
   "source": [
    "class MyClass\n",
    "  attr_accessor :height\n",
    "end\n",
    "\n",
    "a = MyClass.new\n",
    "a.height = 200\n",
    "puts a.height"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### メソッドの別名を定義する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BAR\n",
      "\n",
      "FOO\n",
      "\n"
     ]
    }
   ],
   "source": [
    "class MyClass\n",
    "  def foo\n",
    "    puts 'FOO'\n",
    "  end\n",
    "  alias_method :origin_foo, :foo\n",
    "  def foo\n",
    "    puts 'BAR'\n",
    "  end\n",
    "end\n",
    "\n",
    "a = MyClass.new\n",
    "puts a.foo\n",
    "puts a.origin_foo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 評価する\n",
    "\n",
    "* メソッド\n",
    "    * eval\n",
    "        * 文字列をRubyのコードとして評価する\n",
    "        * 現在のコンテキストで評価する\n",
    "    * module_eval\n",
    "        * モジュールのコンテキストで評価する\n",
    "    * class_eval\n",
    "        * クラスのコンテキストで評価する\n",
    "    * module_exec\n",
    "        * モジュールのコンテキストで評価するときに引数を渡す\n",
    "    * class_exec\n",
    "        * クラスのコンテキストで評価するときに引数を渡す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### 文字列「puts 'Hello'」がRubyのコードとして評価される\n",
    "eval(\"puts 'Hello'\")\n",
    "\n",
    "### bindingオブジェクトを生成する\n",
    "def sample\n",
    "  aaa = 1\n",
    "  binding\n",
    "end\n",
    "\n",
    "### トップレベルからsampleメソッドのローカル変数aaaを呼び出そうとしてもエラーになる\n",
    "#eval(\"p aaa\")\n",
    "\n",
    "### bindingオブジェクトを第二引数で指定すると参照できる\n",
    "eval(\"p aaa\", sample)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OK\n"
     ]
    }
   ],
   "source": [
    "### 空のクラス\n",
    "class Product\n",
    "end\n",
    "\n",
    "### attr_accessorメソッドでvar1～5という属性を作成\n",
    "1.upto(5) do |i|\n",
    "  Product.class_eval { attr_accessor :\"var#{i}\"}\n",
    "end\n",
    "product = Product.new\n",
    "product.var4 = \"OK\"\n",
    "puts product.var4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bar\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "### Arrayクラスにfooメソッドを追加\n",
    "Array.class_eval do\n",
    "  def foo\n",
    "    'bar'\n",
    "  end\n",
    "end\n",
    "puts [].foo\n",
    "\n",
    "class MyClass\n",
    "  CONST = 1\n",
    "end\n",
    "MyClass.class_exec(3) {|i| puts i + self::CONST}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## クラス変数を扱う\n",
    "\n",
    "* メソッド\n",
    "    * class_variables\n",
    "        * 定義されているクラス変数の一覧を返す\n",
    "    * class_variable_defined?\n",
    "        * 指定されたクラス変数が定義されているかどうかを返す\n",
    "    * class_variable_get\n",
    "        * クラス変数を取得する\n",
    "    * class_variable_set\n",
    "        * クラス変数をセットする\n",
    "    * remove_class_variable\n",
    "        * クラス変数を削除する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:@@a, :@@c, :@@d, :@@b]\n",
      "true\n",
      "1\n",
      "5\n",
      "[:@@a, :@@c, :@@d, :@@b]\n",
      "[:@@a, :@@c, :@@d]\n"
     ]
    }
   ],
   "source": [
    "class MyClass\n",
    "  @@a = 1\n",
    "  @@b = \"foo\"\n",
    "  @@c = :bar\n",
    "end\n",
    "\n",
    "puts MyClass.class_variables\n",
    "puts MyClass.class_variable_defined?(:@@a)\n",
    "puts MyClass.class_variable_get(:@@a)\n",
    "puts MyClass.class_variable_set(:@@d, 5)\n",
    "puts MyClass.class_variables\n",
    "MyClass.remove_class_variable(:@@b)\n",
    "puts MyClass.class_variables\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## モジュール機能を取り込む\n",
    "\n",
    "* メソッド\n",
    "    * include\n",
    "        * クラスやモジュール、オブジェクトにモジュールの機能を追加する\n",
    "        * クラスとそのインスタンスが対象\n",
    "    * extend\n",
    "        * クラスやモジュール、オブジェクトにモジュールの機能を追加する\n",
    "        * そのオブジェクトのみ\n",
    "    * included\n",
    "        * includeによってモジュールの機能が取り込まれたときに実行されるメソッド\n",
    "    * extended\n",
    "        * extendによってモジュールの機能が取り込まれたときに実行されるメソッド\n",
    "    * include?\n",
    "        * モジュールをインクルードしているかどうかを確認\n",
    "    * included_modules\n",
    "        * インクルードしているモジュールの一覧を表示\n",
    "    * autoload\n",
    "        * 未定義の定数が参照されたときに自動的に特定のファイルをロードするように設定\n",
    "            * 参照されたタイミングで読み込みを行う\n",
    "            * requireは初めから読み込んでおく\n",
    "    * autoload?\n",
    "        * ファイルがロードされていないときはそのパス名を返す\n",
    "        * ロードされている、もしくはautoloadが指定されていなければnilを返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bar\n",
      "bar\n",
      "[MyMethods, MyModule, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel]\n",
      "true\n"
     ]
    }
   ],
   "source": [
    "### モジュールの作成\n",
    "module MyMethods\n",
    "  def bar\n",
    "    'bar'\n",
    "  end\n",
    "end\n",
    "\n",
    "### クラスにモジュールのメソッドを追加して実行\n",
    "class MyClass\n",
    "  include MyMethods\n",
    "end\n",
    "puts MyClass.new.bar\n",
    "\n",
    "### インスタンスにモジュールのメソッドを追加して実行\n",
    "class NewMyMethods\n",
    "end\n",
    "n = NewMyMethods.new\n",
    "n.extend(MyMethods)\n",
    "puts n.bar\n",
    "\n",
    "puts MyClass.included_modules\n",
    "puts MyClass.include?(MyMethods)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass has included MyModule\n",
      "instance has extended MyModule\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "#<NewMyMethods:0x007f43b2cd1188>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### モジュールの作成\n",
    "module MyModule\n",
    "  def self.included(object)\n",
    "    puts \"#{object} has included #{self}\"\n",
    "  end\n",
    "\n",
    "  def self.extended(object)\n",
    "    puts \"instance has extended #{self}\"\n",
    "  end\n",
    "end\n",
    "\n",
    "class MyClass\n",
    "  include MyModule\n",
    "end\n",
    "\n",
    "class NewMyMethods\n",
    "end\n",
    "n = NewMyMethods.new\n",
    "n.extend(MyModule)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## モジュール関数にする\n",
    "\n",
    "* メソッド\n",
    "    * module_function\n",
    "        * モジュールで定義されているメソッドを、モジュール関数として扱えるようにする\n",
    "        * 引数\n",
    "            * メソッド名\n",
    "                * そのメソッドがモジュール関数になる\n",
    "            * 指定なし\n",
    "                * それ以降に定義されたメソッドがモジュール関数になる"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"bar\""
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### モジュールの関数にする\n",
    "module MyMethods2\n",
    "  def bar\n",
    "    'bar'\n",
    "  end\n",
    "  module_function :bar\n",
    "end\n",
    "MyMethods2.bar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 祖先クラスを取得する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Array, JSON::Ext::Generator::GeneratorMethods::Array, Enumerable, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Array.ancestors"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
